﻿namespace CloudNinja.Metering.Tests.DataRepository
{
    using System;
    using System.Collections.Generic;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Threading.Tasks;

    using CloudNinja.Metering.Data.Dapper;
    using CloudNinja.Metering.Data.Models;

    using Dapper;

    using Xunit;

    public class MeteringDataRespositoryTests : MeteringRepositoryFixture
    {
        [Fact]
        public void CanAddToSameDay()
        {
            var repository = new MeteringDataRepository(MeteringRepositoryFixture.ConnectionString, this.sqlRetryPolicy);

            this.RemoveMeterData();
            var data = this.GetTestMeterData();
            Assert.False(this.GetTestMeterData().Any(meterData => meterData.TenantId.Contains("testtenant")));

            // Add two meter points for two consecutive hours directly to the table. UTC is adjusted.
            this.AddMeterDataForSameTenantForOneDay();
            data = this.GetTestMeterData();
            Assert.Equal(2, data.Count(meterData => meterData.TenantId.Contains("testtenant")));

            // Add a new one for this hour
            repository.Write("bwe", "testtenant1", 10, this.PresentHour, 1);

            // Since still the same day, there must be the same number of rows
            data = this.GetTestMeterData();
            Assert.Equal(2, data.Count(meterData => meterData.TenantId.Contains("testtenant")));

            // But need to be updated.
            Assert.Equal(1, data.Count(d => (d.Count == 2 && Math.Abs(d.Value - 11) < 0.0001)));

            this.RemoveMeterData();
            Assert.False(this.GetTestMeterData().Any(meterData => meterData.TenantId.Contains("testtenant")));
        }

        [Fact]
        public void CanAddToNextDay()
        {
            var repository = new MeteringDataRepository(MeteringRepositoryFixture.ConnectionString, this.sqlRetryPolicy);

            this.RemoveMeterData();
            var data = this.GetTestMeterData();
            Assert.False(data.Any(meterData => meterData.TenantId.Contains("testtenant")));

            // We already have to points for this hour for two meter types and one for the past hour
            this.AddMeterDataForSameTenantForOneDay();
            
            repository.Write("bwe", "testtenant1", 10, this.NextDay, 1);

            // Since we are adding a meter data for the next day, there must be 4
            data = this.GetTestMeterData();
            Assert.Equal(3, data.Count(meterData => meterData.TenantId.Contains("testtenant")));

            this.RemoveMeterData();
            Assert.False(this.GetTestMeterData().Any(meterData => meterData.TenantId.Contains("testtenant")));
        }


        [Fact]
        public void CanGetMonthlyViewData()
        {
            var repository = new MeteringDataRepository(MeteringRepositoryFixture.ConnectionString, this.sqlRetryPolicy);

            this.RemoveMeterData();
            var data = this.GetTestMeterData();
            Assert.False(data.Any(meterData => meterData.TenantId.Contains("testtenant")));

            // We already have to points for this hour for two meter types and one for the past hour
            this.AddMeterDataForSameTenantForOneDay();

            repository.Write("bwe", "testtenant1", 10, this.NextDay, 1);

            var results = repository.GetMonthlyViewAsync();

            // Since we are adding a meter data for the next day, there must be 4
            var monthlyViewData = this.GetTestMonthlyViewData();
            Assert.Equal(2, monthlyViewData.Count(meterData => meterData.TenantId.Contains("testtenant")));

            this.RemoveMeterData();
            Assert.False(data.Any(meterData => meterData.TenantId.Contains("testtenant")));
        }

        [Fact]
        public async Task CanGetMonthlyViewDataAsync()
        {
            var repository = new MeteringDataRepository(MeteringRepositoryFixture.ConnectionString, this.sqlRetryPolicy);

            this.RemoveMeterData();
            var data = this.GetTestMeterData();
            Assert.False(data.Any(meterData => meterData.TenantId.Contains("testtenant")));

            // We already have to points for this hour for two meter types and one for the past hour
            this.AddMeterDataForSameTenantForOneDay();

            repository.Write("bwe", "testtenant1", 10, this.NextDay, 1);

            var results = await repository.GetMonthlyViewAsync();

            // Since we are adding a meter data for the next day, there must be 4
            var monthlyViewData = this.GetTestMonthlyViewData();
            Assert.Equal(2, monthlyViewData.Count(meterData => meterData.TenantId.Contains("testtenant")));

            this.RemoveMeterData();
            Assert.False(data.Any(meterData => meterData.TenantId.Contains("testtenant")));
        }

        [Fact]
        public void AddTestData()
        {
            var repository = new MeteringDataRepository(MeteringRepositoryFixture.ConnectionString, this.sqlRetryPolicy);
            var data = this.GetTestMeterData();
            if (!data.Any(d => d.TenantId.Contains("tnt_test")))
            {
                repository.Write("bwe", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("dbs", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("stg", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("bwi", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("dbi", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("dbe", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("bwe", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("dbs", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("stg", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("bwi", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("dbi", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                repository.Write("dbe", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(-1), 1);
                                                     
                repository.Write("bwe", "tnt_test1", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("dbs", "tnt_test1", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("stg", "tnt_test1", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("bwi", "tnt_test1", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("dbi", "tnt_test1", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("dbe", "tnt_test1", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("bwe", "tnt_test2", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("dbs", "tnt_test2", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("stg", "tnt_test2", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("bwi", "tnt_test2", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("dbi", "tnt_test2", 30, this.PresentHour.AddMonths(-2), 1);
                repository.Write("dbe", "tnt_test2", 30, this.PresentHour.AddMonths(-2), 1);
                                                     
                repository.Write("bwe", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("dbs", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("stg", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("bwi", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("dbi", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("dbe", "tnt_test1", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("bwe", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("dbs", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("stg", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("bwi", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("dbi", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);
                repository.Write("dbe", "tnt_test2", 30, this.PresentHour.AddMonths(-2).AddDays(1), 1);

                repository.Write("bwe", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("dbs", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("stg", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("bwi", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("dbi", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("dbe", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("bwe", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("dbs", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("stg", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("bwi", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("dbi", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);
                repository.Write("dbe", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(-1), 1);

                repository.Write("bwe", "tnt_test1", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("dbs", "tnt_test1", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("stg", "tnt_test1", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("bwi", "tnt_test1", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("dbi", "tnt_test1", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("dbe", "tnt_test1", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("bwe", "tnt_test2", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("dbs", "tnt_test2", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("stg", "tnt_test2", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("bwi", "tnt_test2", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("dbi", "tnt_test2", 20, this.PresentHour.AddMonths(-1), 1);
                repository.Write("dbe", "tnt_test2", 20, this.PresentHour.AddMonths(-1), 1);

                repository.Write("bwe", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("dbs", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("stg", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("bwi", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("dbi", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("dbe", "tnt_test1", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("bwe", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("dbs", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("stg", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("bwi", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("dbi", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);
                repository.Write("dbe", "tnt_test2", 20, this.PresentHour.AddMonths(-1).AddDays(1), 1);

                repository.Write("bwe", "tnt_test1", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("dbs", "tnt_test1", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("stg", "tnt_test1", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("bwi", "tnt_test1", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("dbi", "tnt_test1", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("dbe", "tnt_test1", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("bwe", "tnt_test2", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("dbs", "tnt_test2", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("stg", "tnt_test2", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("bwi", "tnt_test2", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("dbi", "tnt_test2", 10, this.PresentHour.AddDays(-1), 1);
                repository.Write("dbe", "tnt_test2", 10, this.PresentHour.AddDays(-1), 1);

                repository.Write("bwe", "tnt_test1", 10, this.PresentHour, 1);
                repository.Write("dbs", "tnt_test1", 10, this.PresentHour, 1);
                repository.Write("stg", "tnt_test1", 10, this.PresentHour, 1);
                repository.Write("bwi", "tnt_test1", 10, this.PresentHour, 1);
                repository.Write("dbi", "tnt_test1", 10, this.PresentHour, 1);
                repository.Write("dbe", "tnt_test1", 10, this.PresentHour, 1);
                repository.Write("bwe", "tnt_test2", 10, this.PresentHour, 1);
                repository.Write("dbs", "tnt_test2", 10, this.PresentHour, 1);
                repository.Write("stg", "tnt_test2", 10, this.PresentHour, 1);
                repository.Write("bwi", "tnt_test2", 10, this.PresentHour, 1);
                repository.Write("dbi", "tnt_test2", 10, this.PresentHour, 1);
                repository.Write("dbe", "tnt_test2", 10, this.PresentHour, 1);

                repository.Write("bwe", "tnt_test1", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("dbs", "tnt_test1", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("stg", "tnt_test1", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("bwi", "tnt_test1", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("dbi", "tnt_test1", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("dbe", "tnt_test1", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("bwe", "tnt_test2", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("dbs", "tnt_test2", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("stg", "tnt_test2", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("bwi", "tnt_test2", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("dbi", "tnt_test2", 10, this.PresentHour.AddDays(1), 1);
                repository.Write("dbe", "tnt_test2", 10, this.PresentHour.AddDays(1), 1);
            }
        }
    }
}
